#include <unistd.h>
#include <cerrno>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <sys/poll.h>
#include <sys/epoll.h>
#include <net/if.h>
#include <sys/ioctl.h>

#include "dataDevice.h"
#include "readConfig.h"
#include "unp.h"
#include "netDevice.h"
#include "config.h"
#include "network.h"
#include "signal.h"

using namespace std;

const char ConfigFile[] = "config";
static const int timeout = 10;
static const int retryTimes = 5;

string localWiFiIP;
string gatewayAddr;
string dataDeviceAddress;
dataDevice *device;
string deviceName;
int port;

void sig_brokenPipe(int signo)
{
  cout<<"Broken pipe"<<endl;
}

void _turnOnWiFi()
{
	system("sh rt73usb.sh");
}

void _turnOffWiFi()
{
	system("sh rt73usb_rm.sh");
}

void *networkM(void *null)
{
	cout<<"networkM: start"<<endl;
	string currentMode = "wifi";	//or 433
	udpClient netMS;
	netMS.Bind("", networkMPort);
	char buff[1000];
	string remoteAddr;
	string gateWayAddr;
	
	tcpSocket *tcpClient = NULL;
	
	short unsigned int remotePort;
	int res;
	char lastRandomChar = '\x00';
	
	int nfds;
	const int MAX_EVENTS = 30;
	struct epoll_event ev, events[MAX_EVENTS];
	int epollfd = epoll_create(MAX_EVENTS);
	if(epollfd == -1)
	{
		perror("epoll_create");
		exit(-1);
	}

	//ADD control for tcp server
	ev.events = EPOLLIN;
	ev.data.fd = netMS.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: netMS.getsocket()");
		exit(-1);
	}
	
	int readlen;
	
	while(true)
	{
		nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
		for(int i=0;i<nfds;++i)
		{
			if(events[i].data.fd == netMS.getSocket())
			{
				readlen = netMS.Recvfrom((char *)buff, sizeof(buff), remoteAddr, remotePort);
				/*
				cout<<"--------------------"<<endl;
				cout<<"networkM: remote address "<<remoteAddr<<" port "<<remotePort<<endl;
				for(int i=0;i<readlen;i++)
					printf("%02hhx ", buff[i]);
				cout<<endl;
				cout<<"++++++++++++++++++++"<<endl;
				*/
				cout<<"networkM: remote address "<<remoteAddr<<" port "<<remotePort<<endl;
				if(remoteAddr == "127.0.0.1")
					cout<<"networkM: 433M broadcast"<<endl;
				else
				{
					cout<<"networkM: WiFi broadcast"<<endl;
					if(buff[1] != lastRandomChar)
					{
						cout<<"networkM: New cmd"<<endl;
						if(buff[0] == turnOn433)
							netMS.broadcast(buff, readlen, wirelessInterface, networkMPort);
						lastRandomChar = buff[1];
					}
					else
					{	
						cout<<"networkM: Recv this ctl msg again"<<endl;
						continue;
					}
				}

				switch(buff[0])
				{
					case turnOn433:
						if(currentMode == "wifi")
							netMS.Sendto(&turnOn433, sizeof(turnOn433), "", netMSCommandRPort);

						break;

					case turnOffWiFi:
						if(currentMode == "wifi")
							_turnOffWiFi();
						netMS.Sendto(&turnOffWiFiSuccess, sizeof(turnOffWiFiSuccess), "", netMSCommandRPort);

						currentMode = "433";

						break;

					case turnOnWiFi:
					{
						if(currentMode == "433")
							_turnOnWiFi();
						gateWayAddr = buff + 1;
						cout<<"networkM: Gateway address "<<gateWayAddr<<endl;
						//netMS.Sendto(&turnOnWiFiSuccess, 1, gateWayAddr, networkMPort);
						
						
						sleep(1);
						tcpClient = new tcpSocket();
						cout<<tcpClient->getSocket()<<endl;
						tcpClient->Connect(gateWayAddr, networkMPortTCP);
						/*
						while(true)
						{
							try
							{
								tcpClient->Connect(gateWayAddr, networkMPortTCP);
								cout<<"connect OK!"<<endl;
								break;
							}
							catch(socketError sockErr)
							{
								cout<<"Reconnect..."<<endl;
							}
						}	
						*/
						char sbuff[] = {0x01, turnOnWiFiSuccess};
						tcpClient->Send(sbuff, sizeof(sbuff));

						ev.data.fd = tcpClient->getSocket();
						if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev))
						{
							perror("epoll_ctl: netMServer.epoll_ctl()");
							exit(1);
						}
						break;
					}
					case turnOff433Success:
					{
						cout<<"networkM: close433OK"<<endl;
						cout<<"gateWayAddr:"<<gateWayAddr<<endl;
						char sbuff[] = {0x01, turnOff433Success};
						tcpClient->Send(sbuff, sizeof(sbuff));
						//netMS.Sendto(&turnOff433Success, 1, gateWayAddr, networkMPort);

						currentMode = "wifi";

						break;
					}
				}
			}
			else
			{
				char len;
				res = Readn(tcpClient->getSocket(), &len, 1);
				//cout<<"2: "<<tcpClient->getSocket()<<endl;
				//cout<<"res: "<<res<<endl;
				if(0 == res)
				{	
					//Delete from epoll
					TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_DEL, tcpClient->getSocket(), &ev));
					
					cout<<"delete tcpClient"<<endl;
					delete tcpClient;
					tcpClient = NULL;
					continue;
				}
				
				Readn(tcpClient->getSocket(),buff, len);
				char sbuff[] = {0x01, ack};
				if(buff[0] == ack)
				{
					cout<<"ack"<<endl;
					continue;
				}
				else if(buff[0] == ackEnd)
				{
					delete tcpClient;
					tcpClient = NULL;
					continue;
				}
				else if(buff[0] == turnOff433)
				{
					tcpClient->Send(sbuff, sizeof(sbuff));
					netMS.Sendto(&turnOff433, sizeof(turnOff433), "", netMSCommandRPort);
				}
			}
		}
	}

	return (void*)NULL;
}

void *client433(void* null)
{
	//Create 433M device
	FTTMDevice ndd;
	ndd.setup();
	sleep(1);

	struct sockaddr_in local;
	bzero(&local, sizeof(local));
	local.sin_family = AF_INET;
	local.sin_port = htons(netDeviceListenPort);
	local.sin_addr.s_addr = htonl(INADDR_ANY);

	//sleep(1);
	//Connect to 433M Device
	int dsock = Socket(AF_INET, SOCK_STREAM, 0);
	Connect(dsock, (SA*)&local, sizeof(local));

	int nsock = -1;

	udpClient netMSCommandR;
	netMSCommandR.Bind("127.0.0.1", netMSCommandRPort);

	const int MAX_EVENTS = 3;

	struct epoll_event ev, events[MAX_EVENTS];
	int epollfd = epoll_create(MAX_EVENTS);
	if(epollfd == -1)
	{
		perror("epoll_create");
		exit(-1);
	}

	//ADD control for dsock
	ev.events = EPOLLIN;
	ev.data.fd = dsock;
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, dsock, &ev) == -1)
	{
		perror("epoll_ctl: dsock");
		exit(-1);
	}

	ev.data.fd = netMSCommandR.getSocket();
	if(epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) == -1)
	{
		perror("epoll_ctl: nsock");
		exit(-1);
	}

	char buff[254];

	int nfds, readn;
	char id = 1;
	udpClient netMSC;

	while(true)
	{
		nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
		if(nfds == -1)
		{
			perror("epoll_wait");
			exit(-1);
		}

		for(int n = 0; n < nfds; ++n)
		{
			//Receive data from 433M device
			if(events[n].data.fd == dsock)
			{
				char len;
				Readn(dsock, &len, 1);
				Readn(dsock, buff, len);
				if(buff[0] == 1)	//Msg from sink node
				{
					if(buff[1] == DFLAG && -1 != nsock)
						Send(nsock, buff + 2, len - 2, 0);
					else if(buff[1] == CFLAG)	//Send Control Msg to Control Server
						netMSC.Sendto(buff + 2, len - 2, "127.0.0.1", networkMPort);
				}
				cout<<"433M client: Receive "<<len+1<<" bytes(id + data) from 433M device"<<endl;
			}
			//Receive data from node
			else if(events[n].data.fd == nsock)
			{
				readn = Read(nsock, buff + 3, sizeof(buff) - 3);
				buff[0] = readn + 2;
				buff[1] = id;
				buff[2] = DFLAG;
				Send(dsock, buff, readn + 3, 0);	//Send data

				cout<<"433M client: Send "<<readn<<" bytes(id<1 byte> + data) to 433M device"<<endl;
			}
			else if(events[n].data.fd == netMSCommandR.getSocket())
			{
				char buff[100];
				short unsigned int port;	//useless
				string addr;	//useless
				netMSCommandR.Recvfrom((char *)buff, sizeof(buff), addr, port);
				if(addr == "127.0.0.1")
				{
					bool res;

					switch(buff[0])
					{
						case turnOn433:
							res = ndd.on();	//turn on 433M radio
							if(res)
							{
								char buff[] = {0x04, id, CFLAG, 0x01, turnOn433Success};
								Send(dsock, buff, sizeof(buff), 0);
	
								//connect to node server
								local.sin_port = htons(cmdPort);
								nsock = Socket(AF_INET, SOCK_STREAM, 0);
								Connect(nsock, (SA*)&local, sizeof(local));
								cout<<"*"<<endl;
								
								ev.events = EPOLLIN;
								ev.data.fd = nsock;
								if(epoll_ctl(epollfd, EPOLL_CTL_ADD, nsock, &ev) == -1)
								{
									perror("epoll_ctl: nsock");
									exit(-1);
								}
							}
							break;
						
						case turnOffWiFiSuccess:
						{
							char buff[] = {0x04, id, CFLAG, 0x01, turnOffWiFiSuccess};
							Send(dsock, buff, sizeof(buff), 0);
							break;
						}							

						case turnOff433:
							res = ndd.off();	//turn off 433M radio
							if(res)
								netMSC.Sendto(&turnOff433Success, 1, "127.0.0.1", networkMPort);
						
							TEMP_FAILURE_RETRY(epoll_ctl(epollfd, EPOLL_CTL_DEL, nsock, &ev));
							cout<<"1*"<<endl;
							if(nsock != -1)
								Close(nsock);
							cout<<"1#"<<endl;
							nsock = -1;
							break;
					}
				}
			}
		}
	}

	return (void*)NULL;
}

bool openDataDevice()
{
	while(true)
	{
		if(device)
		{
			delete device;
			device = NULL;
		}

		/* create device */
		if(deviceName == "ethernet")
		{
			device = new ethernetDataDevice(dataDeviceAddress.c_str(), port);
		}
		else if(deviceName == "serial")
		{
			device = new serialDataDevice("/dev/ttyTX0", "115200");
		}
		else
		{
			cout<<"Node: device name error"<<endl;
			break;
		}

		/* open device */
		if(device->open())
			return true;

		sleep(5);
		cout<<"Reconneting...."<<endl;
	}

	return false;
}

int main(int argc, char **argv)
{
	Signal(SIGPIPE, sig_brokenPipe);
	int res;
	device = NULL;

	/* get configure */
	Config configSettings(ConfigFile);
	deviceName = configSettings.Read("dataDeviceName", deviceName);
	
	/*
	 * Get wireless interface address
	 */ 
	int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	struct ifreq ifr;

	bzero(&ifr, sizeof(ifr));
	strcpy(ifr.ifr_name, wirelessInterface.c_str());

	if(-1 == ioctl(sockfd, SIOCGIFADDR, &ifr))
	{
		perror("udpClient::broadcast ioctl()");
		exit(-1);
	}

	char paddr[INET_ADDRSTRLEN];
	inet_ntop(AF_INET, &((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr, paddr, INET_ADDRSTRLEN);
	localWiFiIP = paddr;

	/*
	 * if data device is ethernet, get address and port
	 */
	if(deviceName == "ethernet")
	{
		dataDeviceAddress = configSettings.Read("dataDeviceAddress", dataDeviceAddress);
		port =  configSettings.Read("dataDevicePort", 0);
	}

	/*
	 * Open data device
	 */

	tcpServer server;
	cout<<"Node: Listen on port "<<cmdPort<<endl;
	server.Listen(cmdPort);
	int fd = server.getSocket();

	char buff[65536];

	pthread_t thread433client;
	Pthread_create(&thread433client, NULL, client433, NULL);
		
	pthread_t threadnetworkM;
	Pthread_create(&threadnetworkM, NULL, networkM, NULL);

	struct sockaddr_in cliaddr;
	socklen_t clilen = sizeof(cliaddr);

	while(true)
	{
		int connfd = TEMP_FAILURE_RETRY(accept(fd, (struct sockaddr *)&cliaddr, &clilen));
		printf("--------------------------connfd:%d\n", connfd);
		cout<<"Node: Receive connect from: "
		    <<inet_ntop(AF_INET, &cliaddr.sin_addr, paddr, INET_ADDRSTRLEN)
				<<":"
				<<ntohs(cliaddr.sin_port)
				<<endl;

		if(!openDataDevice())
		{
			cout<<"Node: Datadevice error"<<endl;
			exit(1);
		}

		struct pollfd fds[2];
		fds[0].fd = connfd;
		fds[0].events = POLLIN;

		fds[1].fd = device->getFd();
		fds[1].events = POLLIN;

		while(true)
		{
			//res = Poll(fds, 2, timeout*1000);
			res = TEMP_FAILURE_RETRY(poll(fds, 2, -1));
			if(res == 0)
			{
				cout<<"Node: Select timeout"<<endl;
				continue;
			}

			if(fds[0].revents & POLLIN)	//Read data from gateway
			{
				bzero(buff, sizeof(buff));
				res = recv(connfd, buff, sizeof(buff), 0);
				cout<<"Node: Read "<<res<<" from gateway."<<endl;
				if(res == 0)
				{
					cout<<"Node: Socket closed by gateway"<<endl;
					break;
				}
				else if(res < 0)
				{
					if(errno == EINTR)
						continue;

					cout<<"Node: Read error from gateway"<<endl;
					break;
				}
				else if(res > 0)
				{
					cout<<"Node: recv "<<res<<" bytes from gateway"<<endl;
					/*
					for(int i=0;i<res;i++)
						printf("\\x%02x ", buff[i]);
					cout<<endl;
					*/
					/***** write to device*********/
					int wres = device->write(buff, res);
					cout<<"Write to "<<wres<<" to device"<<endl;
					/***** write to device end *********/
					if(wres <= 0)
					{
						if(wres<0 && errno == EINTR)
							continue;
						if(openDataDevice())
						{
							fds[1].fd = device->getFd();
							continue;
						}
						else
						{
							cout<<"Node: Datadevice reopen error"<<endl;
							exit(1);
						}
					}

					//Test
					//send(connfd, buff, res, 0);

					//cout<<"Node sendto gateway."<<endl;
				}
			}

			if(fds[1].revents & POLLIN)	//Read data from device
			{
				bzero(buff, sizeof(buff));
				/***** read from device*******/
				//cout<<"Read from device......."<<endl;
				int res = device->read(buff, sizeof(buff));
				cout<<"Node: Read "<<res<<" bytes from device"<<endl;
				if(res <= 0)
				{
					if(res<0 && errno == EINTR)
						continue;
					if(openDataDevice())
					{
						fds[1].fd = device->getFd();
						continue;
					}
					else
					{
						cout<<"Node: Datadevice reopen error"<<endl;
						exit(1);
					}
				}
				else if(res > 0)
				{
					//cout<<"Read "<<res<<" bytes from device"<<endl;
					/***** read from devicr end *******/

					/**** send data to gateway ******/
					/*
					for(int i = 0;i<res;i++)
						printf("%02hhx ", buff[i]);
					cout<<endl;
					*/
					send(connfd, buff, res, 0);
					cout<<"Send to gateway: "<<res<<" OK"<<endl;

					/**** send data to gateway end ******/
				}
			}//if(fds[1].revents & POLLIN) //Read data from device
		}
		Close(connfd);
		
		if(device)
		{
			delete device;
			device = NULL;
		}

	}//end while(true)

	
	Close(fd); //close listen fd
	return 0;
}//end main

